const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// 匹配到的是结束标签的名字 </xxx>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
// 匹配属性的正则，第一个分组是key，value则是分组3/分组4/分组5
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
// 匹配自闭和的标签
const startTagClose = /^\s*(\/?)>/
export const ELEMENT_TYPE = 1 // 标记这是一个元素
export const TEXT_TYPE = 3 // 标记这是一个文本

export function parseHtml(html){
    const stack = [] // 声明一个栈
    let currentParent;
    let root;

    function createNode(tag,attrs){
        return{
            tag,
            attrs,
            type:ELEMENT_TYPE,
            children:[],
            parent:null
        }
    }


    // 处理开始标签
    function onStartTag(tag,attrs){
        let node = createNode(tag,attrs)
        if(!root){
            root = node
        }
        if(currentParent){
            node.parent = currentParent
            currentParent.children.push(node)
        }
        stack.push(node)
        currentParent = node
    }

    // 处理文本标签
    function onText(text){
        text = text.replace(/\s/g,"")
        text && currentParent.children.push({
            type:TEXT_TYPE,
            text,
            parent:currentParent
        })
    }

    // 处理结束标签
    function onCloseTag(tag){
        stack.pop()
        currentParent = stack[stack.length -1]
    }

    // 前进
    function advance(n){
        html = html.substring(n)
    }
    function parseStart(){
        // 匹配开始标签
        let start = html.match(startTagOpen)
        if(start){
            const match = {
                // 获取到标签名
                tagName:start[1],
                attrs:[]
            }
            // 截取已经匹配到的内容
            advance(start[0].length)
            // 循环匹配剩下的属性，如果不是开始标签的结束位置，则开始匹配属性
            let attr,end;
            while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))){
                match.attrs.push({
                    name:attr[1],
                    value:attr[3] || attr[4] || attr[5] || true
                })
                // 匹配到一点后就删除一点
                advance(attr[0].length)
            }
            if(end){
                advance(end[0].length)
            }
            return match
        }
        return false
    }

    while (html){
        let textEnd = html.indexOf('<');
        // 判断做尖括号的位置，如果是0表示这是一个开始标签
        if(textEnd === 0){
            // 匹配开始标签
            const startTagMatch = parseStart()
            if(startTagMatch){
                onStartTag(startTagMatch.tagName,startTagMatch.attrs)
                continue
            }
            // 匹配结束标签
            let endTagMatch = html.match(endTag)
            if(endTagMatch){
                onCloseTag(endTagMatch[1])
                advance(endTagMatch[0].length)
                continue
            }
        }
        // 进入到这里说明匹配到了文本：{{ xxx }}
        if(textEnd > 0){
            let text = html.substring(0,textEnd)
            if(text){
                onText(text)
                advance(text.length)
            }
        }
    }
    return root
}